실제 로직을 가지고 있는 객체(Visitor)가 로직을 적용할 객체를 방문하면서 실행하는 패턴
일반적인 OOP의 경우, 객체 내에 로직을 메소드로 가지고 있고 대상이 되는 객체가 있을 경우 파라미터로 입력을 받아야 합니다.
방문자 패턴의 경우엔 반대로 대상이 되는 객체(방문 공간)가 행동을 일으키는 객체(방문자)를 입력받으며 방문자 객체에 행동을 위임합니다.
할인마트에서 물건을 판매하고 있습니다. 상품에는 각각의 라벨이 있는데 빨간 라벨 상품과 초록 라벨 상품이 있습니다.
위 같은 차이가 있을 때 고객이 담아온 물건 별로 할인과 적립 혜택이 적용되도록 구현해보았습니다.
각각 상품 클래스 내에 할인과 적립 내용을 저장합니다.
Apply Discount 50%
Add Point 1%
Apply Discount 10%
Add Point 5%
이렇게 할 경우, 순회를 통해 상품 리스트를 순회하며 할인을 적용하고 적립할 수 있습니다.
다만 상품을 사면 상품권을 추가 증정한다는 등의 행동을 추가해야 한다면 모든 Merchandise 구현 클래스들에 대해 수정해주어야 합니다. (단일 책임 원칙 위반)
이를 수정하기 위해 구조와 로직을 구분하여 상품과 혜택(적립, 할인)을 구분했습니다.
혜택을 따로 인터페이스와 구현 클래스를 만들어 적용하였습니다.
BenefitImpl의 메소드를 통해 여러 해택을 적용할 수 있으며 해택을 추가하고자 할 땐 혜택 관련 인터페이스와 클래스를 수정하면 됩니다.
상품 구현 클래스 별로 혜택의 차이가 있기 때문에 구현 클래스를 파라미터로 입력 받도록 오버로딩 했습니다.
하지만 위처럼 상품 구현 객체를 인자로 넣으면 오류가 납니다.
md1과 md2는 Merchandise 객체이기 때문에 구현 클래스를 파라미터로 하는 메소드를 사용할 수 없기 때문입니다.
물론 Merchandise로 생성하지 않고 구현 클래스로 생성하면 정상적으로 작동합니다. 하지만 이렇게 하면 Merchandise interface를 이용하므로써 순회를 이용할 수 있다는 장점이 없어집니다.
방문 공간(상품)에 어떤 방문자(Benefit)가 방문하는지에 따라 행동이 달라집니다.
만약 DiscountBenefit 객체라면 할인 혜택을 위한 동작을, PointBenefit 객체라면 적립 혜택을 위한 동작을 수행합니다.
Apply Discount 50%
Add Point 1%
Apply Discount 10%
Add Point 5%
어떤 Visitor 객체인지에 따라 다른 수행으로 출력되는 것을 확인할 수 있습니다.
만약 여기서 새로운 혜택을 추가한다면 Benefit 인터페이스 구현 클래스를 하나 생성하면 간단하게 새로운 로직을 추가할 수 있습니다.
하지만 상품 라벨을 파란색을 도입한다면 복잡해집니다. 우선 상품 클래스를 생성해야하고 Benfit 인터페이스를 수정해야 합니다. 그와 함께 Benefit 인터페이스를 구현하는 모든 클래스도 수정해야 합니다.
참조
- https://dailyheumsi.tistory.com/216
- https://m.blog.naver.com/PostView.naver?blogId=2feelus&logNo=220664244510&proxyReferer=https:%2F%2Fwww.google.com%2F
- http://ehpub.co.kr/25-%EB%B0%A9%EB%AC%B8%EC%9E%90-%ED%8C%A8%ED%84%B4visitor-pattern/
- 단계별 예시: https://thecodinglog.github.io/design/2019/10/29/visitor-pattern.html
감사합니다.
Text by Chaelin. Photographs by Chaelin, Unsplash.